
module DrunkenMaster
  class Element < VisualComposite

    PAIR_TAGS = [
        :a, :b, :body, :div, :em, :i, :ol, :span, :strong, :script,
        :textarea, :u, :ul, :li
      ]
    
    attr_accessor :tag_name, :child, :attributes, :childNodes

    def initialize tag_name = nil, attributes = {}
      super()
      @attributes = {}
      self.tag_name = tag_name
      @initial_state_children = false
      if attributes.is_a? String
        self.innerHTML = attributes
      elsif attributes.is_a? Hash
        self.attributes = attributes
      elsif attributes == nil
        # do nothing
      else
        raise "Dunno, what to do with second attribute: #{attributes.inspect}"
      end
    end
    
    def attributes= attributes
      attributes.each { |k, v|
        self.send(k.to_s+'=', v)
      }
    end

    def name= name
      super(name)
      attributes['name'] = name
    end
    
    def method_missing(method, *args)
      assert_initialized
      method = method.to_s
      if (method[method.length-1, 1] == "=") && (args.length == 1)
        attr_name = method[0, method.length-1]
        @attributes[attr_name] = args[0]
      elsif method[method.length-1] != "="
        if @attributes.has_key? method
          @attributes[method]
        else
          raise "No attribute or method named '#{method}' for '#{self.class}'."
        end
      else
        super(method, *args)
      end
    end

    def id
      assert_initialized
      @attributes['id'] ||= generate_id
    end

    def id= id
      @attributes['id'] = id
    end

    def generate_id len = 10
      'dm'+Array.new(len/2) { rand(256) }.pack('C*').unpack('H*').first
    end
    
    def className= className
      @attributes['class'] = className
    end

    def className
      @attributes['class'] ||= ""
    end

    def add_class *class_names
      self.className = className.split(' ').push(*class_names).join(' ')
    end


    
    def style style = nil
      @attributes['style'] ||= ""
      if style
        @attributes['style'] << style
        return self
      end
    end
    
    def innerHTML= something
      @children = []
      if something.is_a? Array
        @innerHTML = something
      else
        add something
      end      
    end

    def innerHTML
      @innerHTML || render_children
    end

    def build

    end

    def render
      @initial_state_children = self.children.dup
      #build
      (render_element_with innerHTML).tap {
        self.id = nil
        self.children = @initial_state_children
        if @initial_state_children
          self.children = @initial_state_children
        end
      }
    end
    
    def render_element_with inner_html = []
      if tag_name.nil? || tag_name == ''
        #warn "nil tag_name #{self.class}"
        #puts caller.join("\n")
        return [inner_html]
      end
      arguments = @attributes.collect{ |a, v|
        unless v.nil?
          " #{a}=\"#{v}\""
        else
          nil
        end
      }.compact
      if pair_tag? || inner_html != []
        ["<#{tag_name}"] + arguments + [">"] + inner_html + ["</#{tag_name}>"]
      else
        ["<#{tag_name}#{arguments}/>"]
      end
    end
    
    def inspect
      if @children
        render 
      else
        render_element
      end
    rescue
      super()
    end

    def == other
      other.is_a?(Element) && 
        other.tag_name == self.tag_name &&
        self.attributes == other.attributes
    end

    def pair_tag?
      PAIR_TAGS.include?(self.tag_name.to_sym)
    end

  end
end